import random
import time
import math
import numpy as np
import pandas as pd

import operator


class ItemCF:

    # 构建一个基于用户和物品的推荐
    def Recommendation_s(self,fevalues,uid, iid, similar, base, k=10):
        """减去平均数的计算方法"""
        score = 0
        weight = 0
        user_id_action = fevalues[uid, :]  # 用户uid 对所有商品的行为评分
        item_id_action = fevalues[:, iid]  # 物品iid 得到的所有用户评分

        if base == 'item':  # 基于物品的推荐
            item_id_similar = similar[iid, :]  # 物品iid 与所有物品的相似度
            similar_index = np.argsort(item_id_similar)[-(k + 1):-1]  # 与物品iid最相似的k个物品的index（除了自己）
            item_id_i_mean = np.sum(item_id_action) / item_id_action[item_id_action != 0].size
            # 物品iid得到的所有评分的平均值
            for j in similar_index:  # j是与物品iid最相似的k个物品之一的索引
                if user_id_action[j] != 0:  # user_id_action[j]是用户uid对物品j的打分， 如果为0， 自然没有继续操作的必要
                    # 如果不为0，就需要用到。
                    item_id_j_action = fevalues[:, j]  # item_id_j_action是物品j得到的所有用户的打分
                    item_id_j_mean = np.sum(item_id_j_action) / item_id_j_action[item_id_j_action != 0].size
                    # item_id_j_mean 是物品j得到的打分的平均值
                    score += item_id_similar[j] * (user_id_action[j] - item_id_j_mean)
                    #  物品iid与物品j的相似度*（用户uid对物品j的打分-物品j得到的打分的平均值）
                    weight += abs(item_id_similar[j])
                    #  物品iid与物品j的相似度的绝对值

            if weight == 0:
                return 0
            else:
                return item_id_i_mean + score / weight

        else:
            user_id_similar = similar[uid, :]  # 用户uid 对所有用户的相似度
            similar_index = np.argsort(user_id_similar)[-(k + 1):-1]  # 与用户uid最相似的k个用户的index（除了自己）
            user_id_i_mean = np.sum(user_id_action) / user_id_action[user_id_action != 0].size
            # 用户uid对所有商品打分的平均值
            for j in similar_index:  # j是和用户uid最相似的k个之一用户在用户相似性矩阵similar中的索引
                if item_id_action[j] != 0:  # 物品iid得到的用户j的评分，如果为0， 就没有必要下去

                    user_id_j_action = fevalues[j, :]  # user_id_j_action是用户j对所有物品的打分
                    user_id_j_mean = np.sum(user_id_j_action) / user_id_j_action[user_id_j_action != 0].size
                    # 用户j对所有物品的打分的平均值
                    score += user_id_similar[j] * (item_id_action[j] - user_id_j_mean)
                    #    用户uid与用户j的相似度 *（j用户对物品iid的打分-j这个用户对所有物品打分的平均值（）
                    weight += abs(user_id_similar[j])
                    #       用户uid与用户j的相似度的绝对值

            if weight == 0:
                return 0
            else:
                return user_id_i_mean + score / weight

    # 构建预测函数
    def predict(self,fevalues,similar, base='item'):
        user_cnt = fevalues.shape[0]  # 用户数3318
        item_cnt = fevalues.shape[1]  # 商品数269
        pred = np.zeros((user_cnt, item_cnt))
        for uid in range(user_cnt):
            for iid in range(item_cnt):
                if fevalues[uid, iid] == 0:
                    pred[uid, iid] = self.Recommendation_s(fevalues=fevalues,uid=uid, iid=iid,
                                                           similar=similar,base= base)
        return pred

    def get_recom(self,freq,prediction, k=5):
        recom_df = pd.DataFrame(prediction, columns=freq.columns, index=freq.index)
        recom_df = recom_df.stack().reset_index()
        recom_df.rename(columns={0: "推荐指数"}, inplace=True)
        grouped = recom_df.groupby("买家会员名")
        topk = grouped.apply(self.get_topk, k=k)
        topk = topk.drop(["买家会员名"], axis=1)
        topk.index = topk.index.droplevel(1)
        topk.reset_index(inplace=True)
        return topk

    def get_topk(self,group, k):
        return group.sort_values("推荐指数", ascending=False)[:k]
    def describe_user(self):
        print('my name is')

    def greet_user(self):
        print('hello ')
        self.Recommendation_s(0, 0, 0, 0)
if __name__=='__main__':
    u = ItemCF()
    u.greet_user()